package top.wshape1.shiziapi.uaa.filter;

import com.alibaba.fastjson2.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import top.wshape1.shiziapi.common.constant.AuthConstant;
import top.wshape1.shiziapi.common.constant.BizCodeEnum;
import top.wshape1.shiziapi.common.constant.RedisConstant;
import top.wshape1.shiziapi.common.domain.to.UserTokenTo;
import top.wshape1.shiziapi.common.utils.JwtUtil;
import top.wshape1.shiziapi.common.utils.R;
import top.wshape1.shiziapi.uaa.config.ShiZiApiConfig;
import top.wshape1.shiziapi.uaa.domain.LoginUser;
import top.wshape1.shiziapi.uaa.domain.User;
import top.wshape1.shiziapi.uaa.service.JwtService;
import top.wshape1.shiziapi.uaa.utils.ResponseUtil;

import java.io.IOException;

/**
 * @author Wshape1
 * @version 1.0
 * @description
 * @since 2023-08-24
 */

@Slf4j
@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private JwtService jwtService;

    @Resource
    private ShiZiApiConfig shiZiApiConfig;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String header = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (header == null)
            filterChain.doFilter(request, response);
        else if (!header.startsWith("Bearer ")) {
            R<Void> r = R.instance(BizCodeEnum.NOT_FOUND_TOKEN.code(), BizCodeEnum.NOT_FOUND_TOKEN.msg());
            ResponseUtil.writeResponse(response, r);
        } else {
            String token = header.replace("Bearer ", "");
            //验证token是否正确

            User user;
            try {
                Claims claims = JwtUtil.parseJWT(token, shiZiApiConfig.SIGN_KEY);
                user = JSON.parseObject(claims.getSubject(), User.class);

                // 查询Redis是否存在，否则为非法
                Object o = redisTemplate.opsForValue().get(RedisConstant.USER_JWT_KEY + user.getId());
                if (!token.equals(o)) {
                    R<Void> r = R.instance(BizCodeEnum.ILLEGAL_TOKEN.code(), BizCodeEnum.ILLEGAL_TOKEN.msg());
                    ResponseUtil.writeResponse(response, r);
                }
            } catch (ExpiredJwtException ejw) {
                // Token过期
                Claims claims = ejw.getClaims();
                user = JSON.parseObject(claims.getSubject(), User.class);
                //查询token是否在redis
                Object o = redisTemplate.opsForValue().get(RedisConstant.USER_JWT_KEY + user.getId());
                if (token.equals(o)) {
                    // token在redis则续签
                    UserTokenTo userTokenTo = jwtService.renewJwt(user.getId(), token);
                    BeanUtils.copyProperties(userTokenTo.getUser(), user);
                    response.setHeader(AuthConstant.HEADER_NEW_TOKEN_NAME, userTokenTo.getNewToken());
                } else {
                    // token不在redis，真正过期
                    R<Void> r = R.instance(BizCodeEnum.EXPIRED_TOKEN.code(), BizCodeEnum.EXPIRED_TOKEN.msg());
                    // 设置状态码 401
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    ResponseUtil.writeResponse(response, r);
                }
            } catch (Exception e) {
                R<Void> r = R.instance(BizCodeEnum.ILLEGAL_TOKEN.code(), BizCodeEnum.ILLEGAL_TOKEN.msg());
                ResponseUtil.writeResponse(response, r);
                log.error("TokenVerifyFilter token err: {}\nToken: {}", e.getMessage(), token);
                return;
            }


            LoginUser loginUser = new LoginUser(user);

            // 存入context
            SecurityContext context = SecurityContextHolder.createEmptyContext();
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
            context.setAuthentication(authenticationToken);
            SecurityContextHolder.setContext(context);
            filterChain.doFilter(request, response);
        }
    }
}
